#pragma once

namespace XML {

/**
 * XML document parser.
 */
class CXMLResponseParser
{
public:
	/**
	 * Parser constructor.
	 *
	 * Parameters:
	 *	pDisp - browser (IWebBrowser2)
	 */
	CXMLResponseParser(IDispatch* pDisp);
	CXMLResponseParser(const CComBSTR &xml);

	CComBSTR GetNodeValue(WCHAR* path, WCHAR* node) const
	{
		return m_pParser ? m_pParser->GetNodeValue(path, node) : CComBSTR();
	}

	CComBSTR GetNodeAttribute(WCHAR* path, WCHAR* node, WCHAR* attribute) const
	{
		return m_pParser ? m_pParser->GetNodeAttribute(path, node, attribute) : CComBSTR();
	}

	bool operator ! () const
	{
		return !m_pParser;
	}

	CComBSTR GetSourceXML() const
	{
		return m_pParser ? m_pParser->GetSourceXML() : CComBSTR();
	}

	~CXMLResponseParser();

private:
	bool Initialize(IDispatch* pDisp);
	bool Initialize(const CComBSTR &xml);

	/**
	 * Abstract parser
	 */
	class Parser
	{
	protected:
		Parser(){}

	public:
		virtual ~Parser(){}

	public:
		virtual CComBSTR GetSourceXML() const = 0;
		virtual CComBSTR GetNodeValue(WCHAR* path, WCHAR* node) const = 0;
		virtual CComBSTR GetNodeAttribute(WCHAR* path, WCHAR* node, WCHAR* attribute) const = 0;
	};

	Parser* m_pParser;

private:
	static CLogger logger;

private:
	/**
	 * XMLDocument parser
	 */
	class XMLDocumentParser : public Parser
	{
	private:
		CComBSTR m_xml;
		CComPtr<IXMLDOMDocument> m_xmlDocument;

	public:
		template <class TDocument>
		XMLDocumentParser(const CComPtr<TDocument> &rXmlDocument)
		{
			if (FAILED(rXmlDocument->get_xml(&m_xml)))
			{
				LOG("Cannot get XML...");
				return;
			}

			if (FAILED(m_xmlDocument.CoCreateInstance( __uuidof(DOMDocument) )) || !m_xmlDocument)
			{
				LOG("FAIL to create XML Document...");
				return;
			}

			VARIANT_BOOL bSuccess = VARIANT_FALSE;
			if (FAILED(m_xmlDocument->loadXML(m_xml, &bSuccess )) || (VARIANT_TRUE != bSuccess))
			{
				LOG("Cannot parse XML...");
				return;
			}
		}

		CComBSTR GetSourceXML() const
		{
			return m_xml;
		}

		CComBSTR GetNodeValue(WCHAR* path, WCHAR* node) const;
		CComBSTR GetNodeAttribute(WCHAR* path, WCHAR* node, WCHAR* attribute) const;

	private:
		static CLogger logger;
	};

	/**
	 * Text parser.
	 *
	 * Parses result from text if XML property is unavailable in document.
	 */
	class TextParser : public Parser
	{
	private:
		CComBSTR m_bsDocument;

	public:
		TextParser(const CComBSTR &bsDocument)
			: m_bsDocument(bsDocument)
		{
		}

		CComBSTR GetNodeValue(WCHAR* path, WCHAR* node) const;

		CComBSTR GetNodeAttribute(WCHAR* path, WCHAR* node, WCHAR* attribute) const
		{
			//TODO: implement me
			return CComBSTR();
		}

		CComBSTR GetSourceXML() const
		{
			return m_bsDocument;
		}

	private:
		static CLogger logger;
	};
};

} // namespace XML